package com.ruibo.modules.system.service.impl;

import com.ruibo.exception.BadRequestException;
import com.ruibo.utils.*;
import com.ruibo.modules.system.criteria.DeptQueryCriteria;
import com.ruibo.modules.system.domain.SysDept;
import com.ruibo.modules.system.domain.vo.TreeSelect;
import com.ruibo.modules.system.repository.SysDeptRepository;
import com.ruibo.modules.system.service.dto.SysDeptDto;
import com.ruibo.modules.system.service.mapper.SysDeptMapper;
import com.ruibo.modules.system.service.SysDeptService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author sunfeng
 * @since 2020-04-17 13:35:47
 */
@Slf4j
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "dept")
public class SysDeptServiceImpl implements SysDeptService {

    private final SysDeptRepository sysDeptRepository;

    private final SysDeptMapper sysDeptMapper;

    private final RedisUtils redisUtils;

    @Override
    @Cacheable(key = "'deptId:' + #p0")
    public SysDeptDto findById(Long deptId) {
        log.info("从数据库加载数据");
        SysDept sysDept = sysDeptRepository.getOne(deptId);
        SysDeptDto sysDeptDto = sysDeptMapper.toDto(sysDept);
        LinkedList<String> areaValue = new LinkedList<>();
        // 设置市级编码
        if (!StringUtils.isEmpty(sysDept.getCityCode())) {
            areaValue.add(sysDept.getCityCode());
        }
        // 设置县级编码
        if (!StringUtils.isEmpty(sysDept.getCountyCode())) {
            areaValue.add(sysDept.getCountyCode());
        }
        sysDeptDto.setAreaValue(areaValue);
        return sysDeptDto;
    }

    @Override
    public List<SysDeptDto> findAll() {

        return this.sysDeptMapper.toDto(this.sysDeptRepository.findAll());

    }

    /**
     * 根据pid部门数据
     *
     * @param pid
     * @return
     */
    @Override
    @Cacheable(key = "'pid:' + #p0")
    public List<SysDeptDto> findByPid(Long pid) {
        return this.sysDeptMapper.toDto(this.sysDeptRepository.findByParentId(pid));
    }


    @Override
    public Set<SysDept> findByRoleId(Long roleId) {

        return sysDeptRepository.findBySysRoles_Id(roleId);
    }


    /**
     * 查询部门管理数据
     *
     * @param criteria 部门信息
     * @return 部门信息集合
     */
    @Override
    public List<SysDeptDto> selectDeptList(DeptQueryCriteria criteria) {
        List<SysDept> sysDeptList = sysDeptRepository.findAll((root, criteriaQuery, criteriaBuilder) ->
                QueryHelp.getPredicate(root, criteria, criteriaBuilder)
        );
        return this.sysDeptMapper.toDto(sysDeptList);
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param depts 部门列表
     * @return 下拉树结构列表
     */
    @Override
    public List<TreeSelect> buildDeptTreeSelect(List<SysDeptDto> depts) {

        List<SysDeptDto> deptTrees = buildDeptTree(depts);
        return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 构建前端所需要树结构
     *
     * @param depts 部门列表
     * @return 树结构列表
     */
    @Override
    public List<SysDeptDto> buildDeptTree(List<SysDeptDto> depts) {
        List<SysDeptDto> returnList = new ArrayList<SysDeptDto>();
        List<Long> tempList = new ArrayList<Long>();
        for (SysDeptDto dept : depts) {
            tempList.add(dept.getId());
        }
        for (SysDeptDto dept : depts) {
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(dept.getParentId())) {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        if (returnList.isEmpty()) {
            returnList = depts;
        }
        return returnList;
    }

    /**
     * 根据角色id获取部门列表
     *
     * @param roleId
     * @return
     */
    @Override
    public List<SysDeptDto> selectDeptListByRoleId(Long roleId) {
        Set<SysDept> sysDeptSet = sysDeptRepository.findBySysRoles_Id(roleId);

        return sysDeptMapper.toDto(new ArrayList<>(sysDeptSet));
    }

    @Override
    public Collection<? extends Long> getDeptChildren(Long deptId, List<SysDept> deptChildren) {
        List<Long> list = new ArrayList<>();
        deptChildren.forEach(dept -> {
                    if (dept != null && dept.getStatus().equals("0")) {
                        List<SysDept> depts = sysDeptRepository.findByParentId(dept.getId());
                        if (deptChildren.size() != 0) {
                            list.addAll(getDeptChildren(dept.getId(), depts));
                        }
                        list.add(dept.getId());
                    }
                }
        );
        return list;
    }

    @Override
    public List<SysDeptDto> queryAll(DeptQueryCriteria criteria, boolean b) {
        Sort sort = new Sort(Sort.Direction.ASC, "orderNum");

        List<SysDept> sysMenuList = sysDeptRepository.findAll((root, criteriaQuery, criteriaBuilder) ->
                QueryHelp.getPredicate(root, criteria, criteriaBuilder), sort);
        return sysDeptMapper.toDto(sysMenuList);
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<SysDeptDto> list, SysDeptDto t) {
        // 得到子节点列表
        List<SysDeptDto> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysDeptDto tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                for (SysDeptDto n : childList) {
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysDeptDto> getChildList(List<SysDeptDto> list, SysDeptDto t) {
        List<SysDeptDto> tlist = new ArrayList<SysDeptDto>();
        Iterator<SysDeptDto> it = list.iterator();
        while (it.hasNext()) {
            SysDeptDto n = (SysDeptDto) it.next();
            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysDeptDto> list, SysDeptDto t) {
        return getChildList(list, t).size() > 0;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysDeptDto insert(SysDeptDto sysDept) {
        SysDept dept = new SysDept();

        UpdateUtil.copyNullProperties(sysDept, dept);
        if (sysDept.getAreaValue().size() == 2) {
            LinkedList<String> areaValues = sysDept.getAreaValue();
            dept.setCityCode(areaValues.get(0));
            dept.setCountyCode(areaValues.get(1));
        }
        // 清理缓存
        redisUtils.del("dept::pid:" + (dept.getParentId() == null ? 0 : dept.getParentId()));
        return this.sysDeptMapper.toDto(
                this.sysDeptRepository.save(dept));


    }


    @Override
    //如果返回值null，下次进行该key值查询时，还会查一次数据库，此时相当于@CacheEvict注解;
    //如果返回值不为null，此时会进行该key值缓存的更新，更新缓存值为返回的数据
    //@Cacheable 不同的是，它每次都会触发真实方法的调用
    @CachePut(key = "'deptId:' + #sysDeptDto.getId()")
    @Transactional(rollbackFor = Exception.class)
    public SysDeptDto update(SysDeptDto sysDeptDto) {
        // 旧的部门
        Long oldPid = findById(sysDeptDto.getId()).getParentId();
        Long newPid = sysDeptDto.getParentId();
        if (sysDeptDto.getParentId() != null && sysDeptDto.getId().equals(sysDeptDto.getParentId())) {
            throw new BadRequestException("上级不能为自己");
        }
        SysDept sysDept = sysDeptRepository.findById(sysDeptDto.getId()).orElseGet(SysDept::new);
        //验证要更新的数据是否存在
        ValidationUtil.isNull(sysDept.getId(), "Dept", "id", sysDeptDto.getId());
        UpdateUtil.copyNullProperties(sysDeptDto, sysDept);
        if (sysDeptDto.getAreaValue().size() == 2) {
            LinkedList<String> areaValues = sysDeptDto.getAreaValue();
            sysDept.setCityCode(areaValues.get(0));
            sysDept.setCountyCode(areaValues.get(1));
        }
        // 清理缓存
        delCaches(sysDeptDto.getId(), oldPid, newPid);

        return sysDeptDto;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Long deptId) {

        try {
            this.sysDeptRepository.deleteById(deptId);
            // 清理缓存
            delCaches(deptId, null, null);
        } catch (Exception ex) {
            return false;
        }
        return true;

    }

    /**
     * 清理缓存
     *
     * @param id     /
     * @param oldPid /
     * @param newPid /
     */
    public void delCaches(Long id, Long oldPid, Long newPid) {
        log.info("dept::deptId:" + id);
        redisUtils.del("dept::deptId:" + id);
        redisUtils.del("dept::pid:" + (oldPid == null ? 0 : oldPid));
        redisUtils.del("dept::pid:" + (newPid == null ? 0 : newPid));
    }
}
